package edu.chl.grupp11.aegas.utils;

import java.util.LinkedList;

/**
 * Created by: Pelle Evensen email: pelle@evensen.org
 * 
 * A (partially) compatible replacement for
 * <code>@link java.util.Observable java.util.Observable</code>.
 * 
 * The class addresses two shortcomings of <code>java.util.Observable</code>;
 * <ul>
 * <li>Lacking the possibility to set the sending (observable) object to
 * anything but the concrete implementation providing the observable-property.
 * <li>Adheres to <strong>some</strong> interface, in this case
 * {@link Observable}.
 * </ul>
 * 
 * @author evensen
 * 
 * @param <S>
 *            Type of the Observable class.
 * @param <M>
 *            Type of the message sent on updates.
 */
public class ObservableAdapter<S, M> implements Observable<S, M> {

	private LinkedList<Observer<S, M>> observers;
	private Observable<S, M> parent;
	private boolean hasChanged;

	/**
	 * 
	 * @param parent
	 */
	public ObservableAdapter() {
		this(null);
	}

	/**
	 * 
	 * @param parent
	 */
	public ObservableAdapter(final Observable<S, M> parent) {
		this.observers = new LinkedList<Observer<S, M>>();
		if (parent != null) {
			this.parent = parent;
		} else {
			this.parent = this;
		}
		this.hasChanged = false;
	}

	@Override
	public final boolean addObserver(final Observer<S, M> l) {
		if (!this.observers.contains(l)) {
			this.observers.add(l);
			return true;
		}

		return false;
	}

	public void clearChanged() {
		synchronized (this.observers) {
			this.hasChanged = false;
		}
	}

	@Override
	public final boolean deleteObserver(final Observer<S, M> l) {
		return this.observers.remove(l);
	}

	@Override
	public final void deleteObservers() {
		this.observers.clear();
	}

	@Override
	public final boolean hasChanged() {
		return this.hasChanged;
	}

	@Override
	/**
	 * @inheritdoc
	 */
	public final void notifyObservers(final M o) {
		synchronized (this.observers) {
			if (this.hasChanged) {
				for (Observer<S, M> observer : this.observers) {
					observer.update(this.parent, o);
				}
			}
			clearChanged();
		}
	}

	public void setChanged() {
		synchronized (this.observers) {
			this.hasChanged = true;
		}
	}

	@Override
	public String toString() {
		if (this.parent == this) {
			// TODO: Use reflections to check for the existence of toString() in
			// the subclass.
			// If we call toString() on the parent and we are the
			// parent, infinite recursion will occur.
			return "ObservableAdapter [hasChanged=" + this.hasChanged
					+ ", observers=" + this.observers + ", parent=SELF ("
					+ this.getClass() + "]";
		}
		return "ObservableAdapter [hasChanged=" + this.hasChanged
				+ ", observers=" + this.observers + ", parent=" + this.parent
				+ "]";
	}

}
